﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace EdiNet
{
    public class DataSegment
    {
        const int INITIAL_ELEMENT_COUNT = 25;
        public readonly string SegId;
        List<DataElement> elements = new List<DataElement>(INITIAL_ELEMENT_COUNT);

        /// <summary>
        /// Initializes a new instance of the DataSegment class
        /// </summary>
        /// <param name="segId">Segment ID</param>
        public DataSegment(string segId)
        {
            this.SegId = segId;
        }

        /// <summary>
        /// Adds a new simple data element to the end of a segment
        /// </summary>
        /// <param name="elementValue"></param>
        public DataElement Add(string elementValue)
        {
            DataElement el = new DataElement(elementValue);
            elements.Add(el);
            return el;
        }

        /// <summary>
        /// Gets the DataElement at a specified position. First element (element with index zero) is the ID of a segment
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public DataElement this[int position]
        {
            get
            {
                if (position == 0)
                    return new DataElement(SegId);
                CreateElements(position);
                return elements[position - 1];
            }
        }

        /// <summary>
        /// Returns the number of data elements in a segment. 
        /// </summary>
        /// <returns></returns>
        public int Count()
        {
            return elements.Count() + 1;
        }

        /// <summary>
        /// Returns string representation of the data element. Uses default separators
        /// </summary>
        /// <param name="componentElementSeparator"></param>
        /// <returns></returns>
        public override string ToString()
        {
            return ToString(EdiReaderWriterSettings.DefaultX12.DataElementSeparatorChar,
                EdiReaderWriterSettings.DefaultX12.ComponentSeparatorChar);
        }

        /// <summary>
        /// Returns string representation of the data element. 
        /// </summary>
        /// <param name="componentElementSeparator"></param>
        /// <returns></returns>
        public string ToString(char dataElementSeparator, char componentElementSeparator)
        {
            if (Count() == 0)
                return SegId;
            StringBuilder res = new StringBuilder(SegId);
            foreach (DataElement el in elements)
                res.Append(dataElementSeparator).Append(el.ToString(componentElementSeparator));
            return res.ToString();
        }

        /// <summary>
        /// Compares segments element by element. For unit tests
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            DataSegment seg = obj as DataSegment;
            if (object.ReferenceEquals(seg, null))
                return false;
            if (SegId != seg.SegId)
                return false;

            return elements.SequenceEqual(seg.elements);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="el"></param>
        /// <returns></returns>
        public static bool operator ==(DataSegment el1, DataSegment el2)
        {
            if (object.ReferenceEquals(el1, null))
                return object.ReferenceEquals(el2, null);
            return el1.Equals(el2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="el1"></param>
        /// <param name="el2"></param>
        /// <returns></returns>
        public static bool operator !=(DataSegment el1, DataSegment el2)
        {
            return !(el1 == el2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return SegId.GetHashCode();
        }




        /// <summary>
        /// Creates empty elements up to specified position
        /// </summary>
        /// <param name="position"></param>
        void CreateElements(int position)
        {
            if (position < 1)
                return;
            for (int i = position - 1 - elements.Count(); i >= 0; --i)
                elements.Add(new DataElement());
        }

        /// <summary>
        /// Populates data elements with default values, padds elements with spaces and zeroes to satisfy minimum length requirements
        /// </summary>
        /// <param name="isa"></param>
        public static void FormatIsaSegment(DataSegment isa)
        {

            if (isa.SegId != "ISA")
                throw new ArgumentException("ISA");
            FormatElement(isa[1], 2, '0');
            FormatElement(isa[2], 10, ' ');
            FormatElement(isa[3], 2, '0');
            FormatElement(isa[4], 10, ' ');
            FormatElement(isa[5], 2, ' ');
            FormatElement(isa[6], 15, ' ');
            FormatElement(isa[7], 2, ' ');
            FormatElement(isa[8], 15, ' ');
            FormatElement(isa[9], 6, '0');
            FormatElement(isa[10], 4, '0');
            if (isa[12].V.StartsWith("50"))
                FormatElement(isa[11], 1, EdiReaderWriterSettings.DefaultX12.RepetitionSeparatorChar);
            else
                FormatElement(isa[11], 1, 'U');
            FormatElement(isa[12], 5, '0');
            FormatElement(isa[13], 9, '0', false);
            FormatElement(isa[14], 1, '0');
            FormatElement(isa[15], 1, 'T');
            FormatElement(isa[16], 1, EdiReaderWriterSettings.DefaultX12.ComponentSeparatorChar);
        }

        static void FormatElement(DataElement el, int minLength, char paddingChar)
        {
            FormatElement(el, minLength, paddingChar, true);
        }
        static void FormatElement(DataElement el, int minLength, char paddingChar, bool padRight)
        {
            if (padRight)
                el.V = el.V.PadRight(minLength, paddingChar);
            else
                el.V = el.V.PadLeft(minLength, paddingChar);
        }
    }
}
